/*
@title: Mania
@author: 13SHR
@tags: []
@description: Catch the notes in rhythm with the music !
@addedOn: 2025-08-06
*/

/******** VARIABLES DEFINITIONS ******/

// Keys
const r1 = '0';
const r2 = '1';
const r3 = '2';
const r4 = '3';
const p1 = '4';
const p2 = '5';
const p3 = '6';
const p4 = '7';

// Notes
const note = 'n';

// Grade on results screen
const grade = ['p', 's', 'a'];

// ;)
const waifu = ['w', 'x', 'y', 'z'];
const heart = 'h';

setLegend(
  [r1, bitmap`
................
................
................
................
................
................
...LLLLLLLLLL...
...L........L...
...L........L...
...LLLLLLLLLL...
................
................
................
................
................
................`],
  [p1, bitmap`
................
................
................
................
................
................
...LLLLLLLLLL...
...L00000000L...
...L00000000L...
...LLLLLLLLLL...
................
................
................
................
................
................`],
  [r2, bitmap`
................
................
................
................
................
................
...8888888888...
...8........8...
...8........8...
...8888888888...
................
................
................
................
................
................`],
  [p2, bitmap`
................
................
................
................
................
................
...8888888888...
...8333333338...
...8333333338...
...8888888888...
................
................
................
................
................
................`],
  [r3, bitmap`
................
................
................
................
................
................
...7777777777...
...7........7...
...7........7...
...7777777777...
................
................
................
................
................
................`],
  [p3, bitmap`
................
................
................
................
................
................
...7777777777...
...7555555557...
...7555555557...
...7777777777...
................
................
................
................
................
................`],
  [r4, bitmap`
................
................
................
................
................
................
...4444444444...
...4........4...
...4........4...
...4444444444...
................
................
................
................
................
................`],
  [p4, bitmap`
................
................
................
................
................
................
...4444444444...
...4DDDDDDDD4...
...4DDDDDDDD4...
...4444444444...
................
................
................
................
................
................`],
  [note, bitmap`
................
................
................
................
................
................
................
....99999999....
....99999999....
................
................
................
................
................
................
................`],
  [heart, bitmap`
HHHHHHHHHHHHHHHH
HH88H88HHHHHHHHH
H8888888HHHHHHHH
H8888888HHHHHHHH
H8888888HHHHHHHH
HH88888HHHHHHHHH
HHH888HHHHHHHHHH
HHHH8HHHHHHHHHHH
HHHHHHHHHHH8HHHH
HHHHHHHHHH888HHH
HHHHHHHHH88888HH
HHHHHHHH8888888H
HHHHHHHH8888888H
HHHHHHHH8888888H
HHHHHHHHH88H88HH
HHHHHHHHHHHHHHHH`],
  
  [grade[0], bitmap`
................
................
...LLLLLLL......
..LFFFFFFFL.....
.LFFFLL1111111..
.LFFL.166666661.
.LFFF1666111661.
.LFFF1661...111.
..LLF1666111....
....L166666611..
.LLL..116666661.
.LFFLL..1111661.
.LFFF111....161.
..LLL1661111661.
.....166666661..
......1111111...`],
  [grade[1], bitmap`
................
................
.....1111111....
....166666661...
...1666111661...
...1661...111...
...1666111......
...166666611....
....116666661...
......1111661...
...111....161...
...1661111661...
...166666661....
....1111111.....
................
................`],
  [grade[2], bitmap`
................
................
.....1111111....
....144444441...
...14441114441..
...1441...1441..
...1441...1441..
...1441...1441..
...14411111441..
...14444444441..
...14444444441..
...14411111441..
...1441...1441..
...1441...1441..
...1111...1111..
................`],
  
  [waifu[0], bitmap`
................
................
.....00000000000
...007L9L77777L9
...07L9L7777777L
..07L9L777777777
..07LL5775777577
..077L7775777577
.077757752577257
.077757522577225
.0777L7702257207
.0777L7702225207
.07779L702222207
.077700782232287
.07770.0001L1000
..0770...0L5L0..`],
  [waifu[1], bitmap`
................
................
00..............
L700............
9L70............
L9L70...........
7LL70...........
7L770...........
757770..........
757770..........
7L7770..........
7L7770..........
L97770..........
007770..........
.07770..........
.0770...........`],
  [waifu[2], bitmap`
..0770..0L171L0.
...070.025171520
....00.0LL7L7LL0
........0000000.
..........0.0...
................
................
................
................
................
................
................
................
................
................
................`],
  [waifu[3], bitmap`
.0770...........
.070............
.00.............
................
................
................
................
................
................
................
................
................
................
................
................
................`],
)

const music  = tune`
214.28571428571428: C5^214.28571428571428 + C4/214.28571428571428,
214.28571428571428: F4^214.28571428571428,
214.28571428571428: A4^214.28571428571428 + C4/214.28571428571428,
214.28571428571428: F4^214.28571428571428,
214.28571428571428: C5^214.28571428571428 + C4/214.28571428571428,
214.28571428571428: F4^214.28571428571428,
214.28571428571428: A4^214.28571428571428 + C4/214.28571428571428,
214.28571428571428: F4^214.28571428571428,
214.28571428571428: C5^214.28571428571428 + D4/214.28571428571428,
214.28571428571428: G4^214.28571428571428,
214.28571428571428: B4^214.28571428571428 + D4/214.28571428571428,
214.28571428571428: G4^214.28571428571428,
214.28571428571428: C5^214.28571428571428 + D4/214.28571428571428,
214.28571428571428: G4^214.28571428571428,
214.28571428571428: B4^214.28571428571428 + D4/214.28571428571428,
214.28571428571428: G4^214.28571428571428,
214.28571428571428: C5^214.28571428571428 + E4/214.28571428571428,
214.28571428571428: E4^214.28571428571428,
214.28571428571428: B4^214.28571428571428 + E4/214.28571428571428,
214.28571428571428: E4^214.28571428571428,
214.28571428571428: C5^214.28571428571428 + E4/214.28571428571428,
214.28571428571428: E4^214.28571428571428,
214.28571428571428: B4^214.28571428571428 + E4/214.28571428571428,
214.28571428571428: E4^214.28571428571428,
214.28571428571428: C5^214.28571428571428 + E4/214.28571428571428,
214.28571428571428: E4^214.28571428571428,
214.28571428571428: A4^214.28571428571428 + E4/214.28571428571428,
214.28571428571428: E4^214.28571428571428,
214.28571428571428: C5^214.28571428571428 + E4/214.28571428571428,
214.28571428571428: E4^214.28571428571428,
214.28571428571428: A4^214.28571428571428 + E4/214.28571428571428,
214.28571428571428: E4^214.28571428571428,
214.28571428571428: C5^214.28571428571428 + C4/214.28571428571428 + F4/214.28571428571428,
214.28571428571428: F4^214.28571428571428,
214.28571428571428: A4^214.28571428571428 + C4/214.28571428571428 + F4/214.28571428571428,
214.28571428571428: F4^214.28571428571428,
214.28571428571428: C5^214.28571428571428 + C4/214.28571428571428 + F4/214.28571428571428,
214.28571428571428: F4^214.28571428571428,
214.28571428571428: A4^214.28571428571428 + C4/214.28571428571428 + F4/214.28571428571428,
214.28571428571428: F4^214.28571428571428,
214.28571428571428: C5^214.28571428571428 + D4/214.28571428571428 + G4/214.28571428571428,
214.28571428571428: G4^214.28571428571428,
214.28571428571428: B4^214.28571428571428 + D4/214.28571428571428 + G4/214.28571428571428,
214.28571428571428: G4^214.28571428571428,
214.28571428571428: C5^214.28571428571428 + D4/214.28571428571428 + G4/214.28571428571428,
214.28571428571428: G4^214.28571428571428,
214.28571428571428: B4^214.28571428571428 + D4/214.28571428571428 + G4/214.28571428571428,
214.28571428571428: G4^214.28571428571428,
214.28571428571428: C5^214.28571428571428 + E4/214.28571428571428 + A4/214.28571428571428,
214.28571428571428: E4^214.28571428571428,
214.28571428571428: B4^214.28571428571428 + E4/214.28571428571428 + A4/214.28571428571428,
214.28571428571428: E4^214.28571428571428,
214.28571428571428: C5^214.28571428571428 + E4/214.28571428571428 + A4/214.28571428571428,
214.28571428571428: E4^214.28571428571428,
214.28571428571428: B4^214.28571428571428 + E4/214.28571428571428 + A4/214.28571428571428,
214.28571428571428: E4^214.28571428571428,
214.28571428571428: C5^214.28571428571428 + E4/214.28571428571428 + A4/214.28571428571428,
214.28571428571428: E4^214.28571428571428,
214.28571428571428: A4^214.28571428571428 + E4/214.28571428571428,
214.28571428571428: E4^214.28571428571428,
214.28571428571428: C5^214.28571428571428 + E4/214.28571428571428 + A4/214.28571428571428,
214.28571428571428: E4^214.28571428571428,
214.28571428571428: A4^214.28571428571428 + E4/214.28571428571428,
214.28571428571428: E4^214.28571428571428,
214.28571428571428: C5-214.28571428571428 + C4/214.28571428571428 + F4/214.28571428571428,
214.28571428571428: F4^214.28571428571428,
214.28571428571428: A4^214.28571428571428 + C4/214.28571428571428 + F4/214.28571428571428,
214.28571428571428: F4^214.28571428571428,
214.28571428571428: C5-214.28571428571428 + C4/214.28571428571428 + F4/214.28571428571428,
214.28571428571428: F4^214.28571428571428,
214.28571428571428: A4^214.28571428571428 + C4/214.28571428571428 + F4/214.28571428571428 + D5-214.28571428571428,
214.28571428571428: F4^214.28571428571428 + E5-214.28571428571428,
214.28571428571428: C5^214.28571428571428 + D4/214.28571428571428 + G4/214.28571428571428,
214.28571428571428: G4^214.28571428571428,
214.28571428571428: B4^214.28571428571428 + D4/214.28571428571428 + G4/214.28571428571428 + E5-214.28571428571428,
214.28571428571428: G4^214.28571428571428,
214.28571428571428: C5^214.28571428571428 + D4/214.28571428571428 + G4/214.28571428571428 + F5-214.28571428571428,
214.28571428571428: G4^214.28571428571428,
214.28571428571428: B4^214.28571428571428 + D4/214.28571428571428 + G4/214.28571428571428 + E5-214.28571428571428,
214.28571428571428: G4^214.28571428571428,
214.28571428571428: C5^214.28571428571428 + E4/214.28571428571428 + A4/214.28571428571428 + E5-214.28571428571428,
214.28571428571428: E4^214.28571428571428,
214.28571428571428: B4^214.28571428571428 + E4/214.28571428571428 + A4/214.28571428571428,
214.28571428571428: E4^214.28571428571428 + C5^214.28571428571428,
214.28571428571428: E4/214.28571428571428 + A4/214.28571428571428,
214.28571428571428: E4^214.28571428571428 + C5^214.28571428571428,
214.28571428571428: B4^214.28571428571428 + E4/214.28571428571428 + A4/214.28571428571428,
214.28571428571428: E4^214.28571428571428 + D5-214.28571428571428,
214.28571428571428: E4/214.28571428571428 + A4/214.28571428571428 + C5-214.28571428571428,
214.28571428571428: E4^214.28571428571428,
214.28571428571428: A4^214.28571428571428 + E4/214.28571428571428,
214.28571428571428: E4^214.28571428571428,
214.28571428571428: C5^214.28571428571428 + E4/214.28571428571428 + A4-214.28571428571428,
214.28571428571428: E4^214.28571428571428,
214.28571428571428: A4^214.28571428571428 + E4/214.28571428571428 + B4-214.28571428571428,
214.28571428571428: E4^214.28571428571428,
214.28571428571428: C5-214.28571428571428 + C4/214.28571428571428 + F4/214.28571428571428,
214.28571428571428: F4^214.28571428571428,
214.28571428571428: A4^214.28571428571428 + C4/214.28571428571428 + F4/214.28571428571428,
214.28571428571428: F4^214.28571428571428,
214.28571428571428: C5^214.28571428571428 + C4/214.28571428571428 + F4/214.28571428571428 + D5-214.28571428571428,
214.28571428571428: F4^214.28571428571428 + E5-214.28571428571428,
214.28571428571428: A4^214.28571428571428 + C4/214.28571428571428 + F4/214.28571428571428,
214.28571428571428: F4^214.28571428571428 + D5-214.28571428571428,
214.28571428571428: C5^214.28571428571428 + D4/214.28571428571428 + G4/214.28571428571428,
214.28571428571428: G4^214.28571428571428,
214.28571428571428: B4^214.28571428571428 + D4/214.28571428571428 + G4/214.28571428571428 + D5-214.28571428571428,
214.28571428571428: G4^214.28571428571428,
214.28571428571428: C5-214.28571428571428 + D4/214.28571428571428 + G4/214.28571428571428,
214.28571428571428: G4^214.28571428571428,
214.28571428571428: B4-214.28571428571428 + D4/214.28571428571428 + G4/214.28571428571428,
214.28571428571428: G4^214.28571428571428 + C5-214.28571428571428,
214.28571428571428: C5^214.28571428571428 + E4/214.28571428571428 + A4/214.28571428571428,
214.28571428571428: E4^214.28571428571428,
214.28571428571428: B4^214.28571428571428 + E4/214.28571428571428 + A4/214.28571428571428,
214.28571428571428: E4^214.28571428571428,
214.28571428571428: C5^214.28571428571428 + E4/214.28571428571428 + A4/214.28571428571428,
214.28571428571428: E4^214.28571428571428,
214.28571428571428: B4-214.28571428571428 + E4/214.28571428571428 + A4/214.28571428571428,
214.28571428571428: E4^214.28571428571428 + A4-214.28571428571428,
214.28571428571428: C5^214.28571428571428 + E4/214.28571428571428 + A4/214.28571428571428,
214.28571428571428: E4^214.28571428571428,
214.28571428571428: A4^214.28571428571428 + E4/214.28571428571428,
214.28571428571428: E4^214.28571428571428,
214.28571428571428: C5^214.28571428571428 + E4/214.28571428571428 + A4/214.28571428571428,
214.28571428571428: E4^214.28571428571428,
214.28571428571428: A4^214.28571428571428 + E4/214.28571428571428,
214.28571428571428: E4^214.28571428571428`

/******* RYTHM GAME THINGS ******/

// 1-4 are the rows, 5 is no note
const maniac_map = 
  "20102010312131213020302030103010\
20102010312131213020302030103010\
05550512552535252555555105550515\
25552352552515015555552155555555";

const len = maniac_map.length;

// [perfect, great, miss]
let passed   = [0, 0, 0];
let combo    = 0;
let max_combo = 0;

/* perfect : 300 + 10 * combo
 * great   : 100 + 5  *combo
 * miss    : 0 + combo break
 * combo   : note score times (0.5 * combo)
 */
let score = 0;

let bpm = 280;
let te = 60000/bpm;

const offset = 50; // The offset is highly hardware dependent


/********** INITIAL GAME STATE **********/

setTimeout(() => {
  const playback1 = playTune(music, 1)
}, 6*te + offset);

const states = [
  map`
.........
.........
.........
.........
.........
.0.1.2.3.
.........`,
  map`
wxa
yz.
...`
]

setMap(states[0]);

const pressed_keys = [p1, p2, p3, p4];
const keys_y = getFirst(r1).y;
const keys_x = [
  getFirst(r1).x,
  getFirst(r2).x,
  getFirst(r3).x,
  getFirst(r4).x
];

/********* CUSTOM FUNCTIONS *********/

// Changes the first object from type t1 to type t2,
// without using getFirst
function safe_type_change(t1, t2) {
  for ( const sprite of getAll(t1) ) {
    sprite.type = t2;
  }
}

// Returns true if the key in specified row is pressed 
function is_pressed(row) {
  return getAll(pressed_keys[row]).length === 1;
}

// Function to spawn a note at specified row
function spawnNote(row) {
  addSprite(keys_x[row], 0, note);
}

const center_x  = width();
const eighth_y = Math.round(height() / 4);
const bottom = 2 * height();
// Updates the text to show current combo and score
function updateScore() {
  clearText();
  addText("" + score, {x: 0, y: bottom, color: color`8`});
  addText("" + combo, {x: 9, y: eighth_y, color: color`C`});
}

updateScore();

// Displays the text of the end screen
function finalScore() {
  if (passed[1] === 0 && passed[2] === 0) {
    getFirst(grade[2]).type = grade[0];
  } else if (passed[2] === 0) {
    getFirst(grade[2]).type = grade[1];
  }
  clearText();
  addText("Score: \n" + score, {
    x: 11, 
    y: 2, 
    color: color`0`});
  addText("Perfect: " + passed[0], {
    x: 4,
    y: 8, 
    color: color`6`});
  addText("Great: " + passed[1], {
    x: 4, 
    y: 10, 
    color: color`4`});
  addText("Miss: " + passed[2], {
    x: 4, 
    y: 12, 
    color: color`0`});
  addText("Combo: " + max_combo, {
    x: 4, 
    y: 14, 
    color: color`7`});
}

/*********** USER INPUT *************/

onInput("s", () => {
  safe_type_change(r1, p1);

  setTimeout(() => {
    safe_type_change(p1, r1);
  }, 100);
});

onInput("d", () => {
  safe_type_change(r2, p2);

  setTimeout(() => {
    safe_type_change(p2, r2);
  }, 100);
});

onInput("k", () => {
  safe_type_change(r3, p3);

  setTimeout(() => {
    safe_type_change(p3, r3);
  }, 100);
});

onInput("l", () => {
  safe_type_change(r4, p4);

  setTimeout(() => {
    safe_type_change(p4, r4);
  }, 100);
});

/************** TIMERS ***************/

// Check if a note is on range of a press, then 
// if a key is down, or if got past the keys' line
setInterval(() => {
  const notes = getAll(note);
  let removed = [0, 0, 0, 0] // Ensure only one note is removed with one press
  
  for (const n of notes) {
    const d = Math.abs(n.y - keys_y)
    if ( d <= 2 ) {
      const row = keys_x.indexOf(n.x);
      if (!removed[row] && is_pressed(row)) {
        n.remove();
        removed[row] = 1;
        
        combo += 1;
        if (combo > max_combo) {
          max_combo = combo;
        }

        if (d === 0) { // Perfect
          passed[0] += 1;
          score += 300 + 10 * combo;
        } else if (d === 2) { // Miss (early)
          combo = 0;
          passed[2] += 1;
        } else { // Great
          passed[1] += 1;
          score += 100 + 5  * combo;
        }
        
        updateScore();
      }
    }
  }
}, 100)

// Make all the notes move down every 250ms
setInterval(() => {
  getAll(note).forEach(note => {
    if (note.y === keys_y + 1) { // disappear if already at the bottom, and score a MISS
      note.remove();
      combo = 0;
      passed[2] += 1;
      updateScore();
    } else {
      note.y++; 
  }
  });
}, te);

let count = 0;

// Spawn a note every te
const spawner = setInterval(() => {
  const c = maniac_map[count];
  if (c !== '5') {
    spawnNote(parseInt(c, 10));
  }
  count = count + 1;

  if (count === len) {
    clearInterval(spawner);
    setTimeout( () => {
      setMap(states[1]);
      setBackground(heart);
      finalScore();
    }, 6 * te);
  }
}, te);


afterInput(() => {

});
